package bupt.com.test.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Jc on 2015/12/3.
 * 加载大图的view，可闭环展示大图，但是对于点击添加覆盖物的事件没做处理，所以目前使用时必须调用
 * setDrawableOverlay(false),将点击添加覆盖物功能屏蔽
 */
public class BigIVJust360 extends View {

    /**
     * 局部bitmap
     */
    private BitmapRegionDecoder mDecoder;
    /**
     * 图片宽
     */
    private int mImgWidth;
    /**
     * 图片宽
     */
    private int mImgHeight;
    /**
     * 图片大小（kb）
     */
    private int mImgFileSize;
    /**
     * 是否设置过图片
     */
    private boolean hasImg;
    /**
     * 要绘制的局部矩形（正选矩形）
     */
    private volatile Rect mImgRect = new Rect();
    /**
     * 正选矩形部分滑出边界时的替补矩形
     */
    private volatile Rect mSubstituteRect = new Rect();
    /**
     * 替补矩形的状态：0为不显示，1为在左边，2为在右边
     */
    private int mSubstituteState = 0;
    /**
     * 控件矩形
     */
    private Rect mViewRect = new Rect();
    /**
     * 待绘制矩形和控件矩形间的比例
     * mImgRect/mViewRect
     */
    private float scaleRatio;
    private float minScaleRatio;
    private float maxScaleRatio;
    /**
     * 用于显示的bitmap
     */
    private Bitmap displayBm;
    /**
     * 显示区域的实际宽高
     */
    private int mRealWidth, mRealHeight;

    /**
     * 手势监听
     */
    private MoveGestureDetector mMoveDetector;
    private ScaleGestureDetector mScaleDetector;

    /**
     * 局部解析options
     */
    private BitmapFactory.Options options;

    /**
     * 是否绘制图标覆盖物
     */
    private boolean drawableOverlay = false;
    /**
     * 覆盖物矩形集合
     */
    private List<Overlay> overlays;
    /**
     * 覆盖物默认最多10个
     */
    private int defaultOverlaysLimit;
    /**
     * 添加覆盖物成功
     */
    public static final int RESULT_OK = -1;
    /**
     * 覆盖物数量超出上限
     */
    public static final int OUT_LIMIT = -2;
    /**
     * 点击到已存在的覆盖物
     */
    public static final int INTERSECT = -3;

    private OnOverlayClickListener mListener;

    private Handler mHandler = new Handler();
    private AutoPlayRunnable autoPlayRunnable;
    private boolean isPlaying = false;

    public BigIVJust360(Context context) {
        super(context);
        init(context);
    }

    public BigIVJust360(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public void setDrawableOverlay(boolean drawableOverlay) {
        this.drawableOverlay = drawableOverlay;
        if (overlays == null) {
            overlays = new ArrayList<>();
        }
    }

    public void setOnOverlayClickListener(OnOverlayClickListener listener) {
        mListener = listener;
    }

    public void setImage(InputStream is) {
        try {
            mDecoder = BitmapRegionDecoder.newInstance(is, false);
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, tmpOptions);

            setImgSize(tmpOptions);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) is.close();
            } catch (Exception e) {
                // do nothing
            }
        }
    }

    public void setImage(String filePath) {
        try {
            mDecoder = BitmapRegionDecoder.newInstance(filePath, false);
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, tmpOptions);
            mImgWidth = tmpOptions.outWidth;
            mImgHeight = tmpOptions.outHeight;

            File file = new File(filePath);
            mImgFileSize = (int) (file.length() / 1024);

            setImgSize(tmpOptions);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置图片尺寸并绘制view
     *
     * @param tmpOptions 只测尺寸的options
     */
    private void setImgSize(BitmapFactory.Options tmpOptions) {
        mImgWidth = tmpOptions.outWidth;
        mImgHeight = tmpOptions.outHeight;
        // 标记已设置了图片
        hasImg = true;

        requestLayout();
        invalidate();
    }

    private void init(Context context) {
        // 设置颜色565
        options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        // 设置覆盖物数量上限
        defaultOverlaysLimit = 10;

        // 实例化移动手机监听
        mMoveDetector = new MoveGestureDetector(context,
                new MoveGestureDetector.SimpleOnMoveGestureListener() {
                    @Override
                    public boolean onMove(MoveGestureDetector detector) {
                        moveImg(detector);
                        return true;
                    }
                });
        mScaleDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                    @Override
                    public void onScaleEnd(ScaleGestureDetector detector) {
//                        scaleImg(detector);
                    }
                });
        // 标记还未设置图片
        hasImg = false;
    }

    public void moveImg(MoveGestureDetector detector) {
        PointF d = detector.getFocusDelta();
        int offsetX = (int)(d.x / scaleRatio + 0.5);
        int offsetY = (int)(d.y / scaleRatio + 0.5);

        mImgRect.offset(-offsetX, 0);
        if(mSubstituteState > 0) {
            mSubstituteRect.offset(-offsetX, 0);
        }
        checkWidth();
//        drawDisplayBitmap();

        mImgRect.offset(0, -offsetY);
        mSubstituteRect.offset(0, -offsetY);
        checkHeight();
        drawDisplayBitmap();
    }

    // TODO: 2016/1/6 有问题，未完成
    private void scaleImg(ScaleGestureDetector detector) {
        float tempS = scaleRatio;
        float s = detector.getScaleFactor();
        scaleRatio = scaleRatio / s;

        if(scaleRatio < minScaleRatio) {
            scaleRatio = minScaleRatio;
        }
        if(scaleRatio > maxScaleRatio) {
            scaleRatio = maxScaleRatio;
        }

        float scaleChange = scaleRatio / tempS;

        if (scaleRatio != 1.0f) {
            mImgRect.left = (int) (mImgRect.left / scaleChange + 0.5f);
            mImgRect.top = (int) (mImgRect.top / scaleChange + 0.5f);
            mImgRect.right = (int) (mImgRect.right / scaleChange + 0.5f);
            mImgRect.bottom = (int) (mImgRect.bottom / scaleChange + 0.5f);

            if(mSubstituteState > 0) {
                mSubstituteRect.left = (int) (mSubstituteRect.left / scaleChange + 0.5f);
                mSubstituteRect.top = (int) (mSubstituteRect.top / scaleChange + 0.5f);
                mSubstituteRect.right = (int) (mSubstituteRect.right / scaleChange + 0.5f);
                mSubstituteRect.bottom = (int) (mSubstituteRect.bottom / scaleChange + 0.5f);
            }
        }

        checkWidth();
        checkHeight();
        drawDisplayBitmap();
    }

    private void checkWidth() {
        int imgWidth = mImgWidth;

        // 滑出左边界
        if (mImgRect.left < 0 && mImgRect.right > 0) {
            mImgRect.left = 0;
            // 设置替补矩形
            mSubstituteRect.left = imgWidth - (mRealWidth - mImgRect.width());
            mSubstituteRect.right = imgWidth;
            mSubstituteState = 1;
        } else if (mImgRect.right <= 0) {
            // 正选完全滑出屏幕，替补矩形补全正选矩阵的宽度
            mSubstituteRect.left -= mImgRect.width();
            // 正选替代替补矩形
            mImgRect.set(mSubstituteRect);
            // 替补矩形归零
            mSubstituteRect.left = 0;
            mSubstituteRect.right = 0;
            mSubstituteState = 0;
        }

        // 滑出左边界后再滑回去
        if(mSubstituteRect.left < imgWidth && mSubstituteRect.right > imgWidth) {
            // 先更改替补矩形状态
            mSubstituteRect.right = imgWidth;
            // 在更改正选矩形状态
            mImgRect.left = 0;
            mImgRect.right = mRealWidth - mSubstituteRect.width();
            mSubstituteState = 1;
        } else if(mSubstituteRect.left >= imgWidth) {
            // 替补矩形完全滑出屏幕，正选矩形补全替补矩形的宽度
            mImgRect.right += mSubstituteRect.width();
            // 替补矩形归零
            mSubstituteRect.left = 0;
            mSubstituteRect.right = 0;
            mSubstituteState = 0;
        }

        // 滑出右边界
        if (mImgRect.left < imgWidth && mImgRect.right > imgWidth) {
            mImgRect.right = imgWidth;
            // 设置替补矩形矩形
            mSubstituteRect.left = 0;
            mSubstituteRect.right = mRealWidth - mImgRect.width();
            // 标识替补矩形位置
            mSubstituteState = 2;
        } else if (mImgRect.left >= imgWidth) {
            // 正选完全滑出屏幕，替补矩形补全正选矩阵的宽度
            mSubstituteRect.right += mImgRect.width();
            // 正选替代替补矩形
            mImgRect.set(mSubstituteRect);
            // 替补矩形归零
            mSubstituteRect.left = 0;
            mSubstituteRect.right = 0;
            mSubstituteState = 0;
        }

        // 滑出右边界后再滑回去
        if(mSubstituteRect.left < 0 && mSubstituteRect.right > 0) {
            // 先更改替补矩形状态
            mSubstituteRect.left = 0;
            // 在更改正选矩形状态
            mImgRect.right = imgWidth;
            mImgRect.left = imgWidth - (mRealWidth - mSubstituteRect.width());
            mSubstituteState = 2;
        } else if(mSubstituteRect.right <= 0) {
            // 替补矩形完全滑出屏幕，正选矩形补全替补矩形的宽度
            mImgRect.left -= mSubstituteRect.width();
            // 替补矩形归零
            mSubstituteRect.left = 0;
            mSubstituteRect.right = 0;
            mSubstituteState = 0;
        }
    }

    private void checkHeight() {
        int imageHeight = mImgHeight;

        if (mImgRect.bottom > imageHeight) {
            mImgRect.bottom = imageHeight;
            mImgRect.top = imageHeight - (int) (getHeight() / scaleRatio + 0.5f);
        }
        if (mImgRect.top < 0) {
            mImgRect.top = 0;
            mImgRect.bottom = (int) (getHeight() / scaleRatio + 0.5f);
        }

        if (mSubstituteRect.bottom > imageHeight) {
            mSubstituteRect.bottom = imageHeight;
            mSubstituteRect.top = imageHeight - (int) (getHeight() / scaleRatio + 0.5f);
        }
        if (mSubstituteRect.top < 0) {
            mSubstituteRect.top = 0;
            mSubstituteRect.bottom = (int) (getHeight() / scaleRatio + 0.5f);
        }
    }

    private void drawDisplayBitmap() {
        createDisplayBitmap();
        invalidate();
    }

    private void createDisplayBitmap() {
        // 使用前先回收旧的bitmap
        if (displayBm != null && !displayBm.isRecycled()) {
            displayBm.recycle();
            displayBm = null;
        }
        displayBm = Bitmap.createBitmap(mRealWidth, mRealHeight, Bitmap.Config.RGB_565);

        Canvas canvas = new Canvas(displayBm);

        Bitmap bm = mDecoder.decodeRegion(mImgRect, options);
        if (mSubstituteState == 0) {
            canvas.drawBitmap(bm, 0, 0, null);
        } else if (mSubstituteState > 0) {
            Bitmap bmConnect = mDecoder.decodeRegion(mSubstituteRect, options);
            if (mSubstituteState == 1) {
                canvas.drawBitmap(bmConnect, 0, 0, null);
                canvas.drawBitmap(bm, mSubstituteRect.width(), 0, null);
            } else if (mSubstituteState == 2) {
                canvas.drawBitmap(bm, 0, 0, null);
                canvas.drawBitmap(bmConnect, mImgRect.width(), 0, null);
            }
        }

        if (drawableOverlay) {
            if (overlays != null && overlays.size() > 0) {
                Rect rectOverlay = new Rect();
                for (Overlay overlay : overlays) {
                    if (mImgRect.contains(overlay.rect)) {
                        rectOverlay.set(overlay.rect);
                        rectOverlay.offset(-mImgRect.left, -mImgRect.top);
                        canvas.drawBitmap(overlay.bm, null,
                                rectOverlay, null);
                    }
                }
            }
        }
        canvas.save();
    }

    /**
     * 添加图标覆盖物
     *
     * @param pointF 点击事件坐标点（Float类型，相对于view）
     * @param imgRes 图标资源ID
     */
    public int addOverlay(PointF pointF, int imgRes) {
        // 除以图片缩放的比率，获取相对于实际图片的坐标
        int centerX = (int)(pointF.x / scaleRatio + mImgRect.left + 0.5f);
        int centerY = (int)(pointF.y / scaleRatio + mImgRect.top + 0.5f);

        int result = getRect2Img(centerX, centerY, imgRes, true);

        if (result == RESULT_OK) {
            drawDisplayBitmap();
        }
        return result;
    }

    /**
     * 添加多个图片覆盖物
     *
     * @param points 覆盖物中心的坐标集合（int类型，相对于整张图片）
     * @param imgRes 图标资源ID
     */
    public void addMultiOverlay(List<Point> points, int imgRes) {
        for (Point point : points) {
            getRect2Img(point.x, point.y, imgRes, false);
        }
        drawDisplayBitmap();
    }

    private int getRect2Img(int centerX, int centerY, int imgRes, boolean singleAdd) {
        // 获取图片bitmap
        Bitmap bm = BitmapUtil.decodeRawResource(getResources(), imgRes);
        int half = (int)(bm.getWidth() / scaleRatio / 2 + 0.5f);

        // 相对于实际图片的矩形
        Rect rect = new Rect(centerX - half, centerY - half, centerX + half, centerY + half);
        // 检查是否有重叠，如果有重叠说明点击到已存在的覆盖物，返回覆盖物的index
        for (int i = 0; i < overlays.size(); i++) {
            Overlay overlay = overlays.get(i);
            if (rect.intersect(overlay.rect)) {
                // 有相交回收bm并返回
                bm.recycle();
                if (mListener != null) {
                    mListener.onOverlayClick(i, overlay);
                }
                return INTERSECT;
            }
        }

        // 检查是否到达上限
        if (overlays.size() == defaultOverlaysLimit) {
            bm.recycle();
            return OUT_LIMIT;
        }

        // 检查边界
        checkOverlayRectSide(half, rect);
        Overlay overlay = new Overlay(bm, rect, new Point(centerX, centerY), half);
        overlays.add(overlay);
        if (singleAdd && mListener != null) {
            mListener.onAddOverlayClick(overlay);
        }
        return RESULT_OK;
    }

    private void checkOverlayRectSide(int half, Rect rect) {
        if (rect.right > mImgWidth) {
            rect.right = mImgWidth;
            rect.left = mImgWidth - half * 2;
        }
        if (rect.left < 0) {
            rect.left = 0;
            rect.right = half * 2;
        }
        if (rect.bottom > mImgHeight) {
            rect.bottom = mImgHeight;
            rect.top = mImgHeight - half * 2;
        }
        if (rect.top < 0) {
            rect.top = 0;
            rect.bottom = half * 2;
        }
    }

    /**
     * 移出覆盖物
     *
     * @param pointF 点击事件坐标点
     * @return 删除成功返回改覆盖物的index，否则返回-1
     */
    public int removeOverlay(PointF pointF) {
        int index = -1;
        if (overlays != null && overlays.size() > 0) {
            for (int i = 0; i < overlays.size(); i++) {
                if (overlays.get(i).rect.contains((int)(pointF.x / scaleRatio + mImgRect.left + 0.5f),
                        (int)(pointF.y / scaleRatio + mImgRect.top + 0.5f))) {
                    index = i;
                    break;
                }
            }
            if (index >= 0) {
                Overlay overlay = overlays.get(index);
                if (overlay.bm != null && !overlay.bm.isRecycled()) {
                    overlay.bm.recycle();
                }
                overlays.remove(index);
                drawDisplayBitmap();
            }
        }
        return index;
    }

    public List<Overlay> getAllOverlay() {
        return overlays;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if(!hasImg) {
            return;
        }

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int imgWidth = mImgWidth;
        int imgHeight = mImgHeight;

        // 计算初始化缩放比例
//        if (imgWidth > width && imgHeight > height) {
//            scaleRatio = 1.0f;
//        } else {
//            if (imgWidth > imgHeight) {
//                scaleRatio = (float) height / (float) imgHeight;
//            } else {
//                scaleRatio = (float) width / (float) imgWidth;
//            }
//        }
        if (imgWidth > imgHeight) {
            scaleRatio = (float) height / (float) imgHeight;
        } else {
            scaleRatio = (float) width / (float) imgWidth;
        }

        // 计算最大及最小缩放比例
        minScaleRatio = scaleRatio / 3;
        maxScaleRatio = scaleRatio;

        mViewRect.left = 0;
        mViewRect.top = 0;
        mViewRect.right = width;
        mViewRect.bottom = height;

        mImgRect.left = (int)(mViewRect.left / scaleRatio + 0.5f);
        mImgRect.top = (int)(mViewRect.top / scaleRatio + 0.5f);
        mImgRect.right = (int)(mViewRect.right / scaleRatio + 0.5f);
        mImgRect.bottom = (int)(mViewRect.bottom / scaleRatio + 0.5f);

        mRealWidth = mImgRect.width();
        mRealHeight = mImgRect.height();

        mSubstituteRect.left = 0;
        mSubstituteRect.top = 0;
        mSubstituteRect.right = (int)(mViewRect.right / scaleRatio + 0.5f);
        mSubstituteRect.bottom = (int)(mViewRect.bottom / scaleRatio + 0.5f);

        createDisplayBitmap();

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(!hasImg) {
            return true;
        }
//        mMoveDetector.onTouchEvent(event);
//        mScaleDetector.onTouchEvent(event);
        if(event.getPointerCount() == 1) {
            mMoveDetector.onTouchEvent(event);
        } else {
            mScaleDetector.onTouchEvent(event);
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(hasImg) {
            canvas.drawBitmap(displayBm, null, mViewRect, null);
        }
    }

    public void release() {
        // 取消全局变量引用
        if (displayBm != null) {
            displayBm = null;
        }
        // 回收Decoder
        if (mDecoder != null && !mDecoder.isRecycled()) {
            mDecoder.recycle();
            mDecoder = null;
        }
        // 回收覆盖物
        if (overlays != null) {
            for (Overlay overlay : overlays) {
                if (overlay.bm != null && !overlay.bm.isRecycled()) {
                    overlay.bm.recycle();
                }
            }
            overlays.clear();
            overlays = null;
        }
        if(autoPlayRunnable != null) {
            mHandler.removeCallbacks(autoPlayRunnable);
        }
        if(isPlaying) {
            isPlaying = false;
        }
    }

    /**
     * 根据view中坐标获取相对大图的坐标
     *
     * @param pointF view中坐标
     */
    public PointF getPointF2Img(PointF pointF) {
        PointF p2Img = new PointF();
        p2Img.x = pointF.x / scaleRatio + mImgRect.left;
        p2Img.y = pointF.y / scaleRatio + mImgRect.top;
        return p2Img;
    }

    public float getScaleRatio() {
        return scaleRatio;
    }

    public interface OnOverlayClickListener {
        void onOverlayClick(int position, Overlay overlay);

        void onAddOverlayClick(Overlay overlay);
    }

    public class Overlay {
        Bitmap bm;
        Rect rect;
        Point point;
        int scaleHalf;

        private Overlay(Bitmap bm, Rect rect, Point point, int scaleHalf) {
            this.bm = bm;
            this.rect = rect;
            this.point = point;
            this.scaleHalf = scaleHalf;
        }

        public Point getCenterPoint2Img() {
            return point;
        }

        /**
         * 获取在屏幕中相对于控件的中心点坐标
         *
         * @return PointF
         */
        public PointF getCenterPointF2View() {
            return new PointF((float) (point.x - mImgRect.left) * scaleRatio,
                    (float) (point.y - mImgRect.top) * scaleRatio);
        }

        /**
         * 获取实际显示的覆盖物图片上边中点坐标
         */
        public PointF getOverlayTopMidPointF2View() {
            PointF pointF = getCenterPointF2View();
            pointF.y -= getOverlayHalf();
            return pointF;
        }

        /**
         * 获取实际显示的覆盖物图片半径
         */
        public int getOverlayHalf() {
            return (int) (scaleHalf * scaleRatio);
        }

    }

    public void startPlay() {

        if(!hasImg) {
            return;
        }

        if(autoPlayRunnable == null) {
            autoPlayRunnable = new AutoPlayRunnable();
        }
        mHandler.postDelayed(autoPlayRunnable, 0);
        isPlaying = true;
    }

    public void stopPlay() {
        if(autoPlayRunnable != null) {
            mHandler.removeCallbacks(autoPlayRunnable);
        }
        isPlaying = false;
    }

    public boolean isPlaying() {
        return isPlaying;
    }

    private class AutoPlayRunnable implements Runnable {

        int speed = 20;
        int delay = 0;

        public AutoPlayRunnable() {
//            speed = mImgWidth / 200;

            if(mImgFileSize <= 400) {
                delay = 150;
            } else if(mImgFileSize <= 500) {
                delay = 100;
            } else if(mImgFileSize <= 600) {
                delay = 50;
            } else if(mImgFileSize <= 700) {
                delay = 20;
            } else if(mImgFileSize <= 800) {
                delay = 15;
            } else if(mImgFileSize <= 900) {
                delay = 10;
            } else if(mImgFileSize <= 1000) {
                delay = 5;
            } else {
                delay = 0;
            }
        }

        @Override
        public void run() {
            mImgRect.offset(speed, 0);
            if(mSubstituteState > 0) {
                mSubstituteRect.offset(speed, 0);
            }
            checkWidth();
            drawDisplayBitmap();
            mHandler.postDelayed(this, delay);
        }
    }

}
